<?php

namespace server\dao;

use yii\helpers\ArrayHelper;
use lengbin\helper\directory\ReadDirHelper;

class Role extends \common\core\server\ActiveRecord
{

    private $auth;

    public $name;

    public $description;

    public $ruleName;

    public function __construct(array $config = [])
    {
        parent::__construct($config);
        $this->auth = \Yii::$app->authManager;
    }

    public static function tableName()
    {
        return '{{%auth_item}}';
    }

    /**
     * @inheritdoc
     */
    public function rules()
    {
        return [
            [['name'], 'required'],
            [['name'], 'string', 'max' => 64],
            [['description', 'ruleName'], 'string'],
            ['ruleName', 'validateRule'],
        ];
    }

    /**
     * @inheritdoc
     */
    public function attributeLabels()
    {
        return [
            'name' => '名称',
            'description' => '描述',
            'ruleName' => '规则名称',
        ];
    }

    /**
     * rule list
     *
     * @return array
     * @throws \yii\base\InvalidConfigException
     */
    public function getRuleList()
    {
        $rules = [];
        $ruleDir = \Yii::getAlias('@common') . DIRECTORY_SEPARATOR . 'core' . DIRECTORY_SEPARATOR . 'rule';
        $reader = new ReadDirHelper($ruleDir);
        $reader->setIsNamespace(true);
        $reader->setNamespace('common\core\rule');
        $ruleFiles = $reader->getFileNames();
        foreach ($ruleFiles as $ruleFile) {
            $rule = \Yii::createObject(['class' => $ruleFile]);
            $rules[$ruleFile] = $rule->name;
        }
        return $rules;
    }

    /**
     * Rule by name
     * @param $name
     *
     * @return mixed
     * @throws \yii\base\InvalidConfigException
     */
    public function getRuleByName($name)
    {
        $rules = $this->getRuleList();
        return ArrayHelper::getValue($rules, $name);
    }

    /**
     * validate rule
     *
     * @param $attribute
     * @param $params
     *
     * @throws \yii\base\InvalidConfigException
     */
    public function validateRule($attribute, $params)
    {
        if (!empty($this->ruleName) && empty($this->getRuleByName($this->ruleName))) {
            $this->addError($attribute, '规则不存在');
        }
    }

    /**
     * Role by name
     *
     * @param $name
     *
     * @return null|\yii\rbac\Role
     * @throws \yii\base\UserException
     */
    public function getRoleByName($name)
    {
        $role = $this->auth->getRole($name);
        if (empty($role)) {
            $this->invalidParamException();
        }
        return $role;
    }

    /**
     * Permission by name
     *
     * @param $name
     *
     * @return null|\yii\rbac\Permission
     * @throws \yii\base\UserException
     */
    public function getPermissionByName($name)
    {
        $permission = $this->auth->getPermission($name);
        if (empty($permission)) {
            $this->invalidParamException();
        }
        return $permission;
    }

    /**
     * get role list
     *
     * @param bool $isPage
     *
     * @return array|\yii\rbac\Role[]
     */
    public function getRoles($isPage = false)
    {
        $role = $this->auth->getRoles();
        return $isPage ? $this->page($role) : $role;
    }

    /**
     * get all role
     *
     * @return array|\yii\rbac\Role[]
     */
    public function getRoleList()
    {
        $data = [];
        $roles = $this->getRoles();
        foreach ($roles as $role) {
            $data[$role->name] = $role->name;
        }
        return $data;
    }

    /**
     * get all permission
     *
     * @param bool $isPage
     *
     * @return array|\yii\rbac\Role[]
     */
    public function getPermissions($isPage = false)
    {
        $permission = $this->auth->getPermissions();
        return $isPage ? $this->page($permission) : $permission;
    }

    public function getPermissionList()
    {
        return self::find()->select('name,type,description')->where(['type' => 2])->andWhere(['not', ['description' => null]])->asArray()->all();
    }

    /**
     * get child roles
     *
     * @param string $name
     *
     * @return array|\yii\rbac\Role[]
     */
    public function getChildRoles($name = null)
    {
        $name = $name ? $name : $this->name;
        return $name ? $this->auth->getChildRoles($name) : [];
    }

    /**
     * get permissions all rule
     *
     * @param string $name
     *
     * @return array|\yii\rbac\Role[]
     */
    public function getChildRolePermission($name = null)
    {
        $name = $name ? $name : $this->name;
        return $name ? $this->auth->getChildren($name) : [];
    }

    /**
     * get role all permissions
     *
     * @param string $name
     *
     * @return array|\yii\rbac\Role[]
     */
    public function getPermissionsByRole($name = null)
    {
        $name = $name ? $name : $this->name;
        return $name ? $this->auth->getPermissionsByRole($name) : [];
    }

    /**
     * create or update role
     *
     * @param array $params
     *
     * @return bool
     * @throws \Exception
     */
    public function updateRole($params)
    {
        $auth = $this->auth->createRole($this->name);
        $auth->description = $this->description;
        $status = $this->auth->getRole($this->name) ? $this->auth->update($this->name, $auth) : $this->auth->add($auth);

        // 先删除已有的角色和权限
        $this->auth->removeChildren($this->auth->getRole($this->name));
        $role = $this->getRoleByName($this->name);
        // 子角色
        $childRoles = ArrayHelper::getValue($params, 'r', []);
        $this->batchAddRoles($role, $childRoles);
        // 权限
        $permissions = ArrayHelper::getValue($params, 'p', []);
        $this->batchAddPermission($role, $permissions);

        return $status;
    }

    /**
     * Create or update permission
     *
     * @param $params
     *
     * @return bool
     * @throws \Exception
     * @throws \yii\base\InvalidConfigException
     */
    public function updatePermission($params)
    {
        $auth = $this->auth->createPermission($this->name);
        $auth->description = $this->description;
        if ($this->ruleName) {
            $auth->ruleName = $this->ruleName;
        }
        $status = $this->auth->getPermission($this->name) ? $this->auth->update($this->name, $auth) : $this->auth->add($auth);
        // Rule, if have not rule , create
        if ($this->ruleName) {
            $rule = $this->auth->getRule($this->ruleName);
            if (empty($rule)) {
                $ruleClass = $this->getRuleByName($this->ruleName);
                $rule = \Yii::createObject(['class' => $ruleClass]);
                $this->auth->add($rule);
            }
        }
        // First delete permission and add permission
        $this->auth->removeChildren($this->auth->getPermission($this->name));
        $permissions = ArrayHelper::getValue($params, 'p', []);
        foreach ($permissions as $permission) {
            $this->auth->addChild($this->getPermissionByName($this->name), $this->getPermissionByName($permission));
        }
        return $status;
    }


    /**
     * Batch add role
     *
     * @param \yii\rbac\Role $role
     * @param array $childRoles
     *
     * @throws \yii\base\Exception
     */
    public function batchAddRoles(\yii\rbac\Role $role, array $childRoles)
    {
        foreach ($childRoles as $childRole) {
            if (!$this->auth->hasChild($role, $this->auth->getRole(($childRole))) && (string)$this->name !== $childRole) {
                $this->auth->addChild($role, $this->auth->getRole(($childRole)));
            }
        }
    }

    /**
     *  Batch add permission
     *
     * @param \yii\rbac\Role $role
     * @param array $permissions
     *
     * @throws \yii\base\Exception
     */
    public function batchAddPermission(\yii\rbac\Role $role, array $permissions)
    {
        foreach ($permissions as $permission) {
            if (!$this->auth->hasChild($role, $this->auth->getPermission(($permission)))) {
                $this->auth->addChild($role, $this->auth->getPermission(($permission)));
            }
        }
    }


    /**
     * Delete permission or role
     *
     * @param $name
     *
     * @return bool
     * @throws \yii\base\UserException
     */
    public function removeRole($name)
    {
        $role = $this->getRoleByName($name);
        $this->auth->remove($role);
        return true;
    }

    /**
     * Delete permission
     *
     * @param $name
     *
     * @return bool
     * @throws \yii\base\UserException
     */
    public function removePermission($name)
    {
        $role = $this->getPermissionByName($name);
        $this->auth->remove($role);
        return true;
    }

    /**
     * Empowerment
     *
     * @param $roleName
     * @param $userId
     *
     * @throws \Exception
     * @throws \yii\base\UserException
     */
    public function assign($roleName, $userId)
    {
        $this->auth->assign($this->getRoleByName($roleName), $userId);
    }

    /**
     *
     * @param $permissionName
     * @param $userId
     *
     * @throws \Exception
     * @throws \yii\base\UserException
     */
    public function assignPermission($permissionName, $userId)
    {
        $this->auth->assign($this->getPermissionByName($permissionName), $userId);
    }

    /**
     * Right to withdraw
     *
     * @param $role
     * @param $userId
     *
     * @throws \yii\base\UserException
     */
    public function revoke($role, $userId)
    {
        if ($this->auth->getAssignment($role, $userId)) {
            $this->auth->revoke($this->getRoleByName($role), $userId);
        }
    }

    /**
     * Right to all withdraw
     *
     * @param $userId
     *
     * @return bool
     */
    public function revokeAll($userId)
    {
        return $this->auth->revokeAll($userId);
    }

    /**
     * Use user id get user's role
     *
     * @param $userId
     *
     * @return \yii\rbac\Role[]
     */
    public function getRolesByUser($userId)
    {
        return $this->auth->getRolesByUser($userId);
    }

    /**
     * table name
     *
     * @return mixed
     */
    public function table()
    {
        return '角色或者权限';
    }
}
